package com.naiterui.ehp.bs.b2c.cart.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.naiterui.ehp.bp.bo.patient.PatientBO;
import com.naiterui.ehp.bp.bo.recommend.MedicationRecomBO;
import com.naiterui.ehp.bp.bo.recommend.MedicationRecomDetailBO;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bs.b2c.address.service.IShippingInfoService;
import com.naiterui.ehp.bs.b2c.cart.constants.CartConstants;
import com.naiterui.ehp.bs.b2c.cart.entity.CartItem;
import com.naiterui.ehp.bs.b2c.cart.exception.CartExceptionCodes;
import com.naiterui.ehp.bs.b2c.cart.repository.CartItemRepository;
import com.naiterui.ehp.bs.b2c.cart.service.ICartService;
import com.naiterui.ehp.bs.b2c.cart.vo.CartGroupVO;
import com.naiterui.ehp.bs.b2c.cart.vo.CartItemVO;
import com.naiterui.ehp.bs.b2c.cart.vo.CartProductVO;
import com.naiterui.ehp.bs.b2c.cart.vo.CartRecomVO;
import com.naiterui.ehp.bs.b2c.cart.vo.CartVO;
import com.naiterui.ehp.bs.b2c.cart.vo.PayMethodVO;
import com.naiterui.ehp.bs.b2c.cart.vo.PriceVO;
import com.naiterui.ehp.bs.b2c.cart.vo.SettlementInvoiceVO;
import com.naiterui.ehp.bs.b2c.cart.vo.SettlementVO;
import com.naiterui.ehp.bs.b2c.cart.vo.ShippingInfoVO;
import com.naiterui.ehp.bs.b2c.common.bo.address.ShippingInfoBO;
import com.naiterui.ehp.bs.b2c.common.bo.cart.CartItemBO;
import com.naiterui.ehp.bs.b2c.common.bo.inventory.StockBO;
import com.naiterui.ehp.bs.b2c.common.bo.order.PayTypeBO;
import com.naiterui.ehp.bs.b2c.common.bo.product.SkuBO;
import com.naiterui.ehp.bs.b2c.common.constants.OrderConstants;
import com.naiterui.ehp.bs.b2c.common.remote.IB2cRemoter;
import com.naiterui.ehp.bs.b2c.inventory.service.IStockService;
import com.naiterui.ehp.bs.b2c.order.fegin.IRecommendFeignClient;
import com.naiterui.ehp.bs.b2c.order.service.IOrderService;
import com.naiterui.ehp.bs.b2c.product.service.ISkuService;

/**
 * @author guoyongxiang
 * @date 2019-05-15 13:44
 * @since 1.0.0
 */
@Service
public class CartServiceImpl implements ICartService {

    private static final Logger LOGGER = LoggerFactory.getLogger(CartServiceImpl.class);

    @Autowired
    private CartItemRepository cartItemRepository;
    @Autowired
    private IStockService stockService;
    @Autowired
    private ISkuService skuService;
    @Autowired
    private IB2cRemoter remoter;
    @Autowired
    private IShippingInfoService shippingInfoService;
    @Autowired
    private IOrderService orderService;
    @Autowired
    private IRecommendFeignClient recommendFeignClient;

    /**
     * 购物车列表分组卡片排序规则
     */
    private static final Integer[] GROUP_SORT = { CartConstants.CART_GROUP_TYPE_RX, CartConstants.CART_GROUP_TYPE_OTC };

    @Override
    public CartVO getInfo(Long userId) throws BusinessException {

        List<CartItem> itemList = this.cartItemRepository.findAllByUserIdOrderByIdDesc(userId);
        Set<Long> skuIds = itemList.stream().map(CartItem::getSkuId).collect(Collectors.toSet());
        Map<Long, List<CartItem>> recomMap = itemList.stream().collect(Collectors.groupingBy(CartItem::getRecomId, LinkedHashMap::new,
                Collectors.toList()));

        // 查询处方信息
        List<Long> recomIds = recomMap.keySet().stream().filter(r -> r != 0).collect(Collectors.toList());
        Map<Long, MedicationRecomBO> recomBOMap = this.remoter.getRecomInfos(recomIds).stream().collect(Collectors.toMap(MedicationRecomBO::getId,
                Function.identity()));
        // 查询商品信息
        Map<Long, SkuBO> skuBOMap = this.skuService.cartSkuList(skuIds);
        // 查询库存信息
        Map<Long, StockBO> stockBOMap = this.stockService.getStocks(skuIds)
                                                         .stream()
                                                         .collect(Collectors.toMap(StockBO::getSkuId, Function.identity()));

        // 处方分组
        List<CartGroupVO> groupList = new ArrayList<>();
        // 勾选商品总价
        int totalPrice = 0;
        List<Long> unselectedItemIds = new ArrayList<>();
        Set<Long> invailRecomIdSet = new HashSet<>();

        // 获取缺货商品处方ID
        itemList.stream().forEach(it -> {
            SkuBO skuBO = skuBOMap.get(it.getSkuId());
            StockBO stockBO = stockBOMap.get(it.getSkuId());
            Integer stockStatus = stockBO != null && stockBO.getRemainQuantity() >= it.getQuantity() ? CartConstants.STOCK_STATUS_NORMAL :
                    CartConstants.STOCK_STATUS_SHORTAGE;

            // 库存不足或下架取消勾选
            if (CartConstants.STOCK_STATUS_NORMAL != stockStatus || !skuBO.getStatus().equals(CartConstants.SKU_STATUS_SALE)) {
                unselectedItemIds.add(it.getId());
                it.setSelected(CartConstants.SKU_SELECTED_NOT);

                // 记录包含失效商品的处方列表
                if (it.getRecomId() > 0) {
                    invailRecomIdSet.add(it.getRecomId());
                }
            }
        });

        // 遍历处方分组
        for (Map.Entry<Long, List<CartItem>> r : recomMap.entrySet()) {

            // 遍历处方商品补全信息
            List<CartItemVO> itemVOList = new ArrayList<>();

            for (CartItem it : r.getValue()) {

                CartItemVO itemVO = new CartItemVO();
                itemVO.setItemId(it.getId());
                itemVO.setQuantity(it.getQuantity());
                itemVO.setSkuId(it.getSkuId());
                itemVO.setRx(it.getRx());

                // 设置库存状态
                StockBO stockBO = stockBOMap.get(it.getSkuId());
                Integer stockStatus = stockBO != null && stockBO.getRemainQuantity() >= it.getQuantity() ? CartConstants.STOCK_STATUS_NORMAL :
                        CartConstants.STOCK_STATUS_SHORTAGE;
                itemVO.setStockStatus(stockStatus);

                // 校验处方状态取消勾选
                if (it.getRecomId() > 0 && it.getSelected() == CartConstants.SKU_SELECTED_YES) {
                    MedicationRecomBO recomBO = recomBOMap.get(it.getRecomId());
                    if (recomBO != null) {
                        if (MedicationRecomBO.RECOM_INVALID_YEAH.equals(recomBO.getInvalid())) {
                            LOGGER.warn("购物车处方已作废 userId:{}, recomId:{}", userId, it.getRecomId());
                            unselectedItemIds.add(it.getId());
                            it.setSelected(CartConstants.SKU_SELECTED_NOT);

                        } else if (recomBO.getExpireAt().getTime() < System.currentTimeMillis()) {
                            LOGGER.warn("购物车处方已失效 userId:{}, recomId:{}", userId, it.getRecomId());
                            unselectedItemIds.add(it.getId());
                            it.setSelected(CartConstants.SKU_SELECTED_NOT);

                        } else if (invailRecomIdSet.contains(it.getRecomId())) {
                            // 处方中任意商品失效取消整张处方勾选
                            unselectedItemIds.add(it.getId());
                            it.setSelected(CartConstants.SKU_SELECTED_NOT);
                        }
                    }
                }

                SkuBO skuBO = skuBOMap.get(it.getSkuId());

                // 处方有无效商品整处方取消勾选
                if (invailRecomIdSet.contains(it.getRecomId())) {
                    unselectedItemIds.add(it.getId());
                    it.setSelected(CartConstants.SKU_SELECTED_NOT);
                }

                itemVO.setSelected(it.getSelected());

                // 设置商品属性
                itemVO.setFullName(skuBO.getFullName());
                itemVO.setIcon(skuBO.getIcon());
                itemVO.setSalePrice(skuBOMap.get(it.getSkuId()).getSalePrice());
                itemVO.setStatus(skuBO.getStatus());
                itemVO.setCreatedAt(it.getCreatedAt());
                itemVOList.add(itemVO);

                if (it.getSelected() == CartConstants.SKU_SELECTED_YES) {
                    totalPrice += skuBO.getSalePrice() * itemVO.getQuantity();
                }
            }

            // 计算分组下勾选商品数量
            long selectedCount = itemVOList.stream().filter(i -> i.getSelected() == CartConstants.SKU_SELECTED_YES).count();

            // 分组类型
            Integer groupType = r.getKey() > 0 ? CartConstants.CART_GROUP_TYPE_RX : CartConstants.CART_GROUP_TYPE_OTC;
            // 分组勾选状态
            Integer groupSelected = selectedCount < 1 ? CartConstants.SKU_SELECTED_NOT : selectedCount < itemVOList.size() ?
                    CartConstants.SKU_SELECTED_PART : CartConstants.SKU_SELECTED_YES;

            CartRecomVO recomVO = null;
            MedicationRecomBO recomBO = recomBOMap.get(r.getKey());
            // 处方分组
            if (r.getKey() > 0) {
                if (recomBO == null) {
                    LOGGER.warn("获取购物车失败-未查询到处方信息");
                    throw new BusinessException(CartExceptionCodes.CART_RECOM_INFO_INVAILD);
                }
                // 处方状态(手动作废或超过有效期)
                int status = CartConstants.CART_RECOM_STATUS_NORMAL;
                if (MedicationRecomBO.RECOM_INVALID_YEAH.equals(recomBO.getInvalid()) || recomBO.getExpireAt()
                                                                                                .getTime() < System.currentTimeMillis()) {
                    status = CartConstants.CART_RECOM_STATUS_INVALID;
                }
                recomVO = CartRecomVO.builder().id(r.getKey()).doctorName(recomBO.getDoctorName())
                                     .serialNumber(recomBO.getSerialNumber())
                                     .expiredTime(recomBO.getExpireAt())
                                     .status(status).build();
            }


            // 组装分组VO
            CartGroupVO groupVO = new CartGroupVO();
            groupVO.setType(groupType);
            groupVO.setSelected(groupSelected);
            groupVO.setItems(itemVOList);
            groupVO.setRecom(recomVO);
            groupList.add(groupVO);
        }


        // 购物车卡片排序
        List<CartGroupVO> groups = this.sortCartGroupVO(groupList);

        if (unselectedItemIds.size() > 0) {
            this.unselected(unselectedItemIds);
        }

        return CartVO.builder().totalPrice(totalPrice).groups(groups).build();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean add(Long userId, Long recomId, Long skuId, Integer quantity) throws BusinessException {

        // 只支持单个处方购买，待购买处方加入购物车之前，先清空购物车
        cartItemRepository.removeByUserId(userId);
        Set<Long> skuIds = new HashSet<>();
        // 加入处方
        if (recomId != null && recomId > 0) {
            List<CartItem> cartItems = new ArrayList<>();
            // 去重逻辑
            List<CartItem> cartItemList = this.cartItemRepository.findAllByUserIdAndRecomId(userId, recomId);
            if (!cartItemList.isEmpty()) {
                // 已存在处方默认勾选
                cartItemList.forEach(it -> {
                    it.setSelected(CartConstants.SKU_SELECTED_DEF);
                    it.setChangedAt(new Date());
                });
                cartItemRepository.update(cartItemList);
                return true;
            }

            // 查询处方
            MedicationRecomBO recomBO = this.remoter.getRecomInfo(recomId);
            Map<Long, MedicationRecomBO> recomBOMap = new HashMap<>();
            recomBOMap.put(recomId, recomBO);
            this.cartVerifyRecom(userId, recomId, recomBOMap);

            List<MedicationRecomDetailBO> detailBOList = this.remoter.getRecomDetail(recomId);
            if (detailBOList == null || detailBOList.isEmpty()) {
                LOGGER.warn("添加购物车失败-处方商品为空 userId:{}, recomId:{}", userId, recomId);
                throw new BusinessException(CartExceptionCodes.CART_RECOM_ITEM_EMPTY);
            }

            skuIds.addAll(detailBOList.stream().map(MedicationRecomDetailBO::getSkuId).collect(Collectors.toSet()));

            // 查询商品信息
            Map<Long, SkuBO> skuBOMap = this.skuService.cartSkuList(new HashSet<Long>(skuIds));

            for (MedicationRecomDetailBO d : detailBOList) {
                SkuBO sku = skuBOMap.get(d.getSkuId());
                if (sku == null) {
                    LOGGER.warn("添加购物车失败-处方商品不存在 userId:{}, recomId:{}, skuId:{}", userId, recomId, skuId);
                    throw new BusinessException(CartExceptionCodes.PRODUCT_INFO_ERROR);
                }
                CartItem cartItem = new CartItem();
                cartItem.setUserId(userId);
                cartItem.setProductId(sku.getProductId());
                cartItem.setSkuId(d.getSkuId());
                cartItem.setRecomId(recomId);
                cartItem.setRx(sku.getRx());
                cartItem.setSelected(CartConstants.SKU_SELECTED_YES);
                cartItem.setQuantity(d.getQuantity());
                cartItem.setCreatedAt(new Date());
                cartItem.setChangedAt(new Date());
                cartItems.add(cartItem);
            }
            this.cartItemRepository.save(cartItems);
        } else {
            recomId = 0L;
            boolean update = false;
            // 自主添加非处方
            skuIds.add(skuId);
            // 查询商品信息
            Map<Long, SkuBO> skuBOMap = this.skuService.cartSkuList(new HashSet<Long>(skuIds));
            SkuBO skuBO = skuBOMap.get(skuId);
            if (skuBO == null) {
                LOGGER.warn("添加购物车失败-商品不存在 userId:{}, skuId:{}", userId, skuId);
                throw new BusinessException(CartExceptionCodes.PRODUCT_INFO_ERROR);
            }

            CartItem cartItem = this.cartItemRepository.findAllByUserIdAndProductIdAndSkuIdAndRecomId(userId, skuBO.getProductId(), skuId,
                    recomId);
            if (cartItem == null) {
                cartItem = new CartItem();
                cartItem.setUserId(userId);
                cartItem.setProductId(skuBO.getProductId());
                cartItem.setSkuId(skuId);
                cartItem.setRecomId(0L);
                cartItem.setRx(skuBO.getRx());
                cartItem.setCreatedAt(new Date());
                cartItem.setQuantity(quantity);
            } else {
                cartItem.setQuantity(cartItem.getQuantity() + quantity);
                update = true;
            }
            cartItem.setSelected(CartConstants.SKU_SELECTED_DEF);
            cartItem.setChangedAt(new Date());

            if (update) {
                this.cartItemRepository.update(cartItem);
            } else {
                this.cartItemRepository.save(cartItem);
            }
        }
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateSelected(Long userId, Integer all, Long recomId, Long itemId, Integer selected) throws BusinessException {

        List<CartItem> cartItems = null;

        // 全选
        if (all != null && all > 0) {
            cartItems = this.cartItemRepository.findAllByUserIdOrderByIdDesc(userId);

        } else if (recomId != null && (itemId == null || itemId == 0)) {
            // 分组选择
            cartItems = this.cartItemRepository.findAllByUserIdAndRecomId(userId, recomId);

        } else {
            CartItem cartItem = this.cartModifyVerify(userId, itemId);
            this.cartItemRepository.update(cartItem);
            return true;
        }

        // 批量更新
        if (cartItems != null && !cartItems.isEmpty()) {
            cartItems.forEach(i -> {
                i.setSelected(selected);
                i.setChangedAt(new Date());
            });
            this.cartItemRepository.update(cartItems);
            return true;
        }

        return false;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateQuantity(Long userId, Long itemId, Integer quantity) throws BusinessException {

        CartItem item = this.cartModifyVerify(userId, itemId);
        item.setQuantity(quantity);
        item.setChangedAt(new Date());
        this.cartItemRepository.update(item);

        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean delete(Long userId, Long recomId, Long itemId) throws BusinessException {

        // 删除处方
        if (recomId != null && recomId > 0) {
            List<CartItem> cartItemList = this.cartItemRepository.findAllByUserIdAndRecomId(userId, recomId);
            if (cartItemList.isEmpty()) {
                return true;
            } else {
                this.cartItemRepository.remove(cartItemList);
                return true;
            }
        }

        // 删除自选非处方
        CartItem item = this.cartItemRepository.get(itemId);
        if (item == null) {
            return true;
        }
        if (!item.getUserId().equals(userId)) {
            LOGGER.warn("删除购物车商品失败-商品项与用户不匹配 userId:{}, itemId:{}, dbUserId:{}", userId, itemId, item.getUserId());
            throw new BusinessException(CartExceptionCodes.CART_ITEM_USER_NOT_MATCH);
        }
        this.cartItemRepository.remove(item);
        return true;
    }

    @Transactional
    @Override
    public SettlementVO settlements(Long userId, Long shippingInfoId) throws BusinessException {

        // 获取商品列表(校验商品)
        List<CartItemBO> cartItemBOList = this.getCartItems(userId);
        List<CartProductVO> products = this.getCartProductVO(cartItemBOList);
        int totalPrice = cartItemBOList.stream().mapToInt(i -> i.getSalePrice() * i.getQuantity()).sum();

        // 获取配送地址
        ShippingInfoBO shippingInfoBO = null;
        if (shippingInfoId != null) {
            shippingInfoBO = this.shippingInfoService.getShippingInfoById(shippingInfoId, userId);
        } else {
            shippingInfoBO = this.shippingInfoService.getDefaultShippingInfo(userId);
        }
        ShippingInfoVO shippingInfoVO = null;
        if (shippingInfoBO != null) {
            shippingInfoVO = new ShippingInfoVO();
            shippingInfoVO.setShippingInfoId(shippingInfoBO.getShippingInfoId());
            shippingInfoVO.setPhone(shippingInfoBO.getPhone());
            shippingInfoVO.setReceiver(shippingInfoBO.getReceiver());
            shippingInfoVO.setFullAddress(shippingInfoBO.getFullAddress());
        }

        SettlementVO settlementVO = new SettlementVO();
        settlementVO.setProducts(products);
        settlementVO.setShippingInfo(shippingInfoVO);
        settlementVO.setShippingMethod("物流配送");

        List<PayTypeBO> payTypeBOList = this.orderService.getPayTypes();
        List<PayMethodVO> payMethod = payTypeBOList.stream()
                                                   .map(p -> PayMethodVO.builder().id(p.getId()).name(p.getName()).tips("").build())
                                                   .collect(Collectors.toList());
        settlementVO.setPayMethods(payMethod);
        settlementVO.setInvoices(this.getInvoiceVOS());
        // 暂不支持优惠券
        settlementVO.setVoucher(null);

        // 设置价格
        PriceVO priceVO = new PriceVO();
        int freight = CartConstants.FREIGHT_DEF;
        int couponPay = 0;
        int realAmount = totalPrice + freight - couponPay;
        priceVO.setTotalAmount(totalPrice);
        priceVO.setFreight(freight);
        priceVO.setCouponPay(couponPay);
        priceVO.setRealAmount(realAmount);
        settlementVO.setPrice(priceVO);

        Integer realNameStatus = this.getPatientRealNameStatus(userId);
        settlementVO.setRealNameStatus(realNameStatus);

        return settlementVO;
    }

    @Override
    public List<CartItemBO> getCartItems(Long userId) throws BusinessException {
        // 获取已勾选购物车列表商品
        List<CartItem> cartItems = this.cartItemRepository.findAllByUserIdAndSelected(userId, CartConstants.SKU_SELECTED_YES);
        if (cartItems.isEmpty()) {
            LOGGER.warn("购物车中没有待结算的商品 userId:{}", userId);
            throw new BusinessException(CartExceptionCodes.CART_SETTLEMENTS_EMPTY);
        }

        // 提取处方ID列表
        List<Long> recomIds = cartItems.stream().filter(c -> c.getRecomId() > 0).map(CartItem::getRecomId).collect(Collectors.toList());

        // 合并商品数量
        Map<Long, Integer> skuQuantityMap = cartItems.stream().collect(Collectors.groupingBy(CartItem::getSkuId,
                Collectors.summingInt(CartItem::getQuantity)));

        // 获取商品信息
        Set<Long> skuIds = skuQuantityMap.keySet();

        Map<Long, SkuBO> skuBOMap = this.skuService.cartSkuList(skuIds);

        // 获取库存信息
        // List<StockBO> stockBOList = this.stockService.getStocks(skuIds);
        // Map<Long, StockBO> stockBOMap = stockBOList.stream().collect(Collectors.toMap(StockBO::getSkuId,
        // Function.identity()));

        // 获取处方信息
        Map<Long, MedicationRecomBO> recomBOMap = new HashMap<>();
        if (!recomIds.isEmpty()) {
            List<MedicationRecomBO> recomBOList = this.remoter.getRecomInfos(recomIds);
            recomBOMap = recomBOList.stream().collect(Collectors.toMap(MedicationRecomBO::getId, r -> r));
        }

        List<CartItemBO> cartItemBOList = new ArrayList<>();
        // 校验已勾选的商品状态、库存、获取处方有效状态
        for (CartItem item : cartItems) {
            // 校验商品状态
            SkuBO sku = this.cartVerifyProduct(userId, item.getSkuId(), skuBOMap);

            // 校验库存状态
            // this.cartVerifyStock(userId, item.getSkuId(), stockBOMap, skuQuantityMap);

            CartItemBO itemBO = new CartItemBO();
            itemBO.setRecomId(0L);
            itemBO.setDoctorId(0L);

            // 校验处方状态
            if (!recomIds.isEmpty() && item.getRecomId() != 0) {
                MedicationRecomBO recomBO = this.cartVerifyRecom(userId, item.getRecomId(), recomBOMap);
                itemBO.setRecomId(recomBO.getId());
                itemBO.setDoctorId(recomBO.getDoctorId());
            }

            itemBO.setItemId(item.getId());
            itemBO.setSkuId(item.getSkuId());
            itemBO.setProductId(sku.getProductId());
            itemBO.setProductName(sku.getProductName());
            itemBO.setRx(sku.getRx());
            itemBO.setFullName(sku.getFullName());
            itemBO.setIcon(sku.getIcon());
            itemBO.setQuantity(item.getQuantity());
            itemBO.setSalePrice(sku.getSalePrice());
            cartItemBOList.add(itemBO);
        }

        return cartItemBOList;
    }

    @Override
    public void clearCart(Long userId, List<Long> itemIds) throws BusinessException {
        List<CartItem> itemList = this.cartItemRepository.findAllByUserIdAndSelected(userId, CartConstants.SKU_SELECTED_YES);
        List<CartItem> removeList = itemList.stream().filter(i -> itemIds.contains(i.getId())).collect(Collectors.toList());
        this.cartItemRepository.remove(removeList);
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void unselected(List<Long> itemIds) {
        Iterable<CartItem> cartItems = this.cartItemRepository.findAllById(itemIds);
        cartItems.forEach(i -> i.setSelected(CartConstants.SKU_SELECTED_NOT));
        this.cartItemRepository.update(cartItems);
    }

    /**
     * 购物车修改校验
     *
     * @param userId
     * @param itemId
     *
     * @throws BusinessException
     */
    private CartItem cartModifyVerify(Long userId, Long itemId) throws BusinessException {

        CartItem item = this.cartItemRepository.get(itemId);
        if (item == null) {
            LOGGER.warn("修改购物车商品数量失败-商品项不存在 userId:{}, itemId:{}", userId, itemId);
            throw new BusinessException(CartExceptionCodes.CART_ITEM_NOT_EXISTS);
        }

        if (!item.getUserId().equals(userId)) {
            LOGGER.warn("修改购物车商品数量失败-商品项与用户不匹配 userId:{}, itemId:{}, dbUserId:{}", userId, itemId, item.getUserId());
            throw new BusinessException(CartExceptionCodes.CART_ITEM_USER_NOT_MATCH);
        }

        // 校验是否为处方商品(处方不允许变更)
        if (item.getRecomId() > 0) {
            LOGGER.warn("修改购物车商品数量失败-处方商品不允许变更 userId:{}, itemId:{}", userId, itemId);
            throw new BusinessException(CartExceptionCodes.CART_RECOM_ITEM_CAN_NOT_MODIFY);
        }
        return item;
    }

    /**
     * 校验商品状态
     *
     * @param userId
     * @param skuId
     * @param skuBOMap
     *
     * @throws BusinessException
     */
    private SkuBO cartVerifyProduct(Long userId, Long skuId, Map<Long, SkuBO> skuBOMap) throws BusinessException {

        SkuBO sku = skuBOMap.get(skuId);
        if (sku == null) {
            LOGGER.warn("购物车商品信息获取失败 userId:{}, skuId:{}", userId, skuId);
            throw new BusinessException(CartExceptionCodes.CART_ITEM_NOT_EXISTS);
        }
        // if (sku.getStatus() != CartConstants.SKU_STATUS_SALE) {
        // LOGGER.warn("购买商品非上架状态 userId:{}, skuId:{}, status:{}", userId, skuId, sku.getStatus());
        // throw new BusinessException(CartExceptionCodes.CART_PRODUCT_NOT_SALE);
        // }
        return sku;
    }

    /**
     * 获取购物车校验库存
     *
     * @param userId
     * @param skuId
     * @param stockBOMap
     * @param skuQuantityMap
     *
     * @throws BusinessException
     */
    private StockBO cartVerifyStock(Long userId, Long skuId, Map<Long, StockBO> stockBOMap,
                                    Map<Long, Integer> skuQuantityMap) throws BusinessException {
        StockBO stock = stockBOMap.get(skuId);
        int buyQuantity = skuQuantityMap.get(skuId);
        if (stock == null) {
            LOGGER.warn("购物车商品库存获取失败 userId:{}, skuId:{}", userId, skuId);
            throw new BusinessException(CartExceptionCodes.GET_PRODUCT_STOCK_FAILED);
        }
        if (stock.getRemainQuantity() < buyQuantity) {
            LOGGER.warn("购买商品缺货 userId:{}, skuId:{}, remainQuantity:{}, buyQuantity:{}", userId, skuId, stock.getRemainQuantity(), buyQuantity);
            throw new BusinessException(CartExceptionCodes.PRODUCT_STOCK_LESS);
        }
        return stock;
    }

    /**
     * 获取购物车校验处方
     *
     * @param userId
     * @param recomId
     * @param recomBOMap
     *
     * @throws BusinessException
     */
    private MedicationRecomBO cartVerifyRecom(Long userId, Long recomId, Map<Long, MedicationRecomBO> recomBOMap) throws BusinessException {

        MedicationRecomBO recom = recomBOMap.get(recomId);
        if (recom == null) {
            LOGGER.warn("获取商品处方信息失败 userId:{}, recomId:{}", userId, recomId);
            throw new BusinessException(CartExceptionCodes.CART_RECOM_INFO_INVAILD);
        }
        if (!recom.getPatientId().equals(userId)) {
            LOGGER.warn("处方患者不匹配 userId:{}, recomId:{}", userId, recomId);
            throw new BusinessException(CartExceptionCodes.CART_RECOM_INFO_INVAILD);
        }
        if (MedicationRecomBO.RECOM_INVALID_YEAH.equals(recom.getInvalid())) {
            LOGGER.warn("购物车处方已作废 userId:{}, recomId:{}", userId, recomId);
            throw new BusinessException(CartExceptionCodes.CART_RECOM_IS_INVAILD);
        }
        if (recom.getExpireAt().getTime() < System.currentTimeMillis()) {
            LOGGER.warn("购物车处方已失效 userId:{}, recomId:{}", userId, recomId);
            throw new BusinessException(CartExceptionCodes.CART_RECOM_IS_EXPIRE);
        }
        if (recom.getStatus() > 0) {
            // 查询处方订单是否未付款
            boolean hasWaitPay = this.orderService.hasWaitPayByRecomId(recomId);
            if (hasWaitPay) {
                LOGGER.warn("购物车处方已成单,未支付 userId:{}, recomId:{}", userId, recomId);
                throw new BusinessException(CartExceptionCodes.CART_RECOM_ALREADY_BUY, "处方订单已提交，请在订单列表完成支付");
            }
            LOGGER.warn("购物车处方已成单 userId:{}, recomId:{}", userId, recomId);
            throw new BusinessException(CartExceptionCodes.CART_RECOM_ALREADY_BUY);
        }
        return recom;
    }

    /**
     * 获取购物车商品列表
     *
     * @param cartItemBOList
     *
     * @return
     *
     * @throws BusinessException
     */
    private List<CartProductVO> getCartProductVO(List<CartItemBO> cartItemBOList) throws BusinessException {

        // 分组合并商品数
        Map<Long, Integer> skuQuantityMap = cartItemBOList.stream().collect(Collectors.groupingBy(CartItemBO::getSkuId,
                Collectors.summingInt(CartItemBO::getQuantity)));
        Map<Long, List<CartItemBO>> itemBOMap = cartItemBOList.stream().collect(Collectors.groupingBy(CartItemBO::getSkuId));
        List<CartProductVO> productVOList = new ArrayList<>();
        for (Map.Entry<Long, Integer> entry : skuQuantityMap.entrySet()) {
            CartProductVO productVO = new CartProductVO();
            CartItemBO itemBO = itemBOMap.get(entry.getKey()).get(0);
            productVO.setSkuId(itemBO.getSkuId());
            productVO.setName(itemBO.getFullName());
            productVO.setIcon(itemBO.getIcon());
            productVO.setRx(itemBO.getRx());
            productVO.setSalePrice(itemBO.getSalePrice());
            productVO.setQuantity(entry.getValue());
            productVOList.add(productVO);
        }
        return productVOList;
    }

    private List<SettlementInvoiceVO> getInvoiceVOS() {
        List<SettlementInvoiceVO> invoiceVOS = new ArrayList<>();
        invoiceVOS.add(SettlementInvoiceVO.builder().type(OrderConstants.INVOICE_TYPE_ELECTRONICS).name("电子发票").build());
        invoiceVOS.add(SettlementInvoiceVO.builder().type(OrderConstants.INVOICE_TYPE_PAPER).name("纸质发票").build());
        return invoiceVOS;
    }

    /**
     * 获取患者实名状态
     *
     * @param patientId
     *
     * @return
     *
     * @throws BusinessException
     */
    @Override
    public Integer getPatientRealNameStatus(Long patientId) throws BusinessException {
        PatientBO patientBO = this.remoter.getPatientInfo(patientId);
        if (patientBO == null) {
            LOGGER.warn("查询用户实名信息失败 patientId:{}", patientId);
            throw new BusinessException(CartExceptionCodes.SETTLEMENT_GET_PATIENT_INFO_FAILED);
        }
        // 身份证实名状态
        if (PatientBO.PATIENT_CERTIFICATION_Y.equals(patientBO.getStatus())) {
            return CartConstants.SETTLEMENT_USER_REAL_NAME_STATUS_PASS;
        }
        return CartConstants.SETTLEMENT_USER_REAL_NAME_STATUS_UNPASS;
    }

    /**
     * 购物车卡片排序
     *
     * @param groupVOList
     *
     * @return
     */
    private List<CartGroupVO> sortCartGroupVO(List<CartGroupVO> groupVOList) {

        // 正常商品列表
        List<CartGroupVO> normalGroupVO = new ArrayList<>();
        // 暂时不可购买商品列表(库存不足、下架)
        List<CartGroupVO> tempCannotBuyGroupVO = new ArrayList<>();
        // 失效商品列表
        List<CartGroupVO> invalidGroupVO = new ArrayList<>();

        for (CartGroupVO groupVO : groupVOList) {
            // 处方失效
            if (groupVO.getRecom() != null && groupVO.getRecom().getStatus().equals(CartConstants.CART_RECOM_STATUS_INVALID)) {
                invalidGroupVO.add(groupVO);
            } else if (this.hasCannotBuySku(groupVO.getItems())) {
                tempCannotBuyGroupVO.add(groupVO);
            } else {
                normalGroupVO.add(groupVO);
            }
        }

        // 重排序列表
        List<CartGroupVO> groupVOListSort = new ArrayList<>();
        groupVOListSort.addAll(normalGroupVO);
        groupVOListSort.addAll(tempCannotBuyGroupVO);
        groupVOListSort.addAll(invalidGroupVO);

        // 按分组类型二次排序
        Map<Integer, List<CartGroupVO>> groupMap = groupVOListSort.stream().collect(Collectors.groupingBy(CartGroupVO::getType));

        return Arrays.stream(GROUP_SORT)
                     .filter(t -> groupMap.get(t) != null)
                     .map(groupMap::get)
                     .flatMap(List::stream)
                     .collect(Collectors.toList());
    }

    /**
     * 校验购物车是否包含不可购买状态的商品
     *
     * @param cartItems
     *
     * @return
     */
    private boolean hasCannotBuySku(List<CartItemVO> cartItems) {
        for (CartItemVO item : cartItems) {
            // 非销售状态商品
            if (!item.getStatus().equals(CartConstants.SKU_STATUS_SALE)) {
                return true;
            }
            // 库存异常商品(缺货)
            if (!item.getStockStatus().equals(CartConstants.STOCK_STATUS_NORMAL)) {
                return true;
            }
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeExpireRequireRecom(Long recomId) throws BusinessException {
        // 处方信息
        MedicationRecomBO medicationRecomBO = remoter.getRecomInfo(recomId);
        if (medicationRecomBO.getExpireAt().after(new Date())) {
            // 未过期，不能清理
            LOGGER.info("清除购物车已过期且已求药的处方, 未过期，不能清理， recomId {}", recomId);
            // throw new BusinessException(CartExceptionCodes.CART_RECOM_NOT_EXPIRE);
            return;
        }
        // 处方在购物车记录
        List<CartItem> items = cartItemRepository.findByRecomId(recomId);
        if (null == items || items.size() == 0) {
            // 不存在购物车，无需清理
            LOGGER.info("清除购物车已过期且已求药的处方, 该处方不存在购物车，无需清理， recomId {}", recomId);
            // throw new BusinessException(CartExceptionCodes.RECOM_NOT_EXIST_CART);
            return;
        }
        // 清理操作
        cartItemRepository.deleteByRecomId(recomId);
    }

    @Override
    @Transactional
    public SettlementVO recomSettlement(Long userId, Long recomId, Long shippingInfoId) throws BusinessException {
        // 清空购物车
        MedicationRecomBO medicationRecomBO = recommendFeignClient.getRecom(recomId);
        if (!userId.equals(medicationRecomBO.getPatientId())) {
            throw new BusinessException(CartExceptionCodes.RECOM_NOT_CURRENT_USER);
        }
        cartItemRepository.removeByUserId(medicationRecomBO.getPatientId());
        // 添加处方到购物车
        this.add(medicationRecomBO.getPatientId(), recomId, null, null);
        // 购物车结算
        SettlementVO settlementVO = this.settlements(medicationRecomBO.getPatientId(), shippingInfoId);
        return settlementVO;
    }
}
